# -*- coding: utf-8 -*-
'''
Created on 2011-4-21
支付宝接口
@author: Yefe
'''
import types
import datetime
from . import mpay_rsa
from urllib.parse import urlencode
from urllib.request import urlopen
from .hashcompat import md5_constructor as md5
from project.settings import alipay_settings as settings, ALIPAY_CALLBACK_ADDR


def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a bytestring version of 's', encoded as specified in 'encoding'.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (type(None), int)):
        return s
    if not isinstance(s, str):
        try:
            return str(s)
        except UnicodeEncodeError:
            if isinstance(s, Exception):
                # An Exception subclass containing non-ASCII data that doesn't
                # know how to print itself properly. We shouldn't raise a
                # further exception.
                return ' '.join([smart_str(arg, encoding, strings_only,
                        errors) for arg in s])
            return str(s).encode(encoding, errors)
    elif isinstance(s, str):
        return s.encode(encoding, errors)
    elif s and encoding != 'utf-8':
        return s.decode('utf-8', errors).encode(encoding, errors)
    else:
        return s


# 网关地址
_GATEWAY = 'https://mapi.alipay.com/gateway.do?'


# 对数组排序并除去数组中的空值和签名参数
# 返回数组和链接串
def params_filter(params):
    ks = list(params.keys())
    ks.sort()
    newparams = {}
    prestr = ''
    for k in ks:
        v = params[k]
        if k not in ('sign', 'sign_type') and v != '':
            newparams[k] = v
            prestr += '%s=%s&' % (k, newparams[k])
    prestr = prestr[:-1]
    return newparams, prestr


# 生成签名结果
def build_mysign(prestr, key, sign_type = 'MD5'):
    if sign_type == 'MD5':
        return md5((prestr + key).encode()).hexdigest()
    return ''


# 即时到账交易接口
def create_direct_pay_by_user(tn, subject, body, total_fee, quantity=1, mweb=0):
    """
    :param mweb: 1时则是支付宝H5的页面，2是使用支付宝扫码模式，else模式为pc页面的模式
    :return:
    """
    params = {}
    if mweb == 1:
        params['service'] = 'alipay.wap.create.direct.pay.by.user'
        params['seller_id'] = settings.ALIPAY_PARTNER
        params['app_pay'] = 'Y'
        params['return_url'] = settings.ALIPAY_RETURN_URL_STUDENT_BUY_ACTUAL % (ALIPAY_CALLBACK_ADDR, tn)
    elif mweb == 2:
        params['service'] = 'create_direct_pay_by_user'
        params['return_url'] = settings.ALIPAY_RETURN_URL_STUDENT_BUY_PRODUCT
        params['qr_pay_mode'] = 2
    else:
        params['service'] = 'create_direct_pay_by_user'
        params['return_url'] = settings.ALIPAY_RETURN_URL_STUDENT_BUY_PRODUCT
    params['payment_type'] = '1'

    # 获取配置文件
    params['partner']           = settings.ALIPAY_PARTNER
    params['seller_email']      = settings.ALIPAY_SELLER_EMAIL
    params['notify_url']        = settings.ALIPAY_NOTIFY_URL_STUDENT_BUY_PRODUCT
    params['_input_charset']    = settings.ALIPAY_INPUT_CHARSET
    params['show_url']          = settings.ALIPAY_SHOW_URL

    # 从订单数据中动态获取到的必填参数
    params['out_trade_no']  = tn        # 请与贵网站订单系统中的唯一订单号匹配
    params['subject']       = subject   # 订单名称，显示在支付宝收银台里的“商品名称”里，显示在支付宝的交易管理的“商品名称”的列表里。
    params['body']          = body      # 订单描述、订单详细、订单备注，显示在支付宝收银台里的“商品描述”里
    params['total_fee']     = total_fee # 订单总金额，显示在支付宝收银台里的“应付总额”里
    
    # 扩展功能参数——网银提前
    params['paymethod'] = 'directPay'   # 默认支付方式，四个值可选：bankPay(网银); cartoon(卡通); directPay(余额); CASH(网点支付)
    params['defaultbank'] = ''          # 默认网银代号，代号列表见http://club.alipay.com/read.php?tid=8681379
    
    # 扩展功能参数——防钓鱼
    params['anti_phishing_key'] = ''
    params['exter_invoke_ip'] = ''

    # 数量
    params['quantity'] = str(quantity)

    # 扩展功能参数——自定义参数
    params['buyer_email'] = ''

    # 扩展功能参数——分润
    params['royalty_type'] = ''
    params['royalty_parameters'] = ''
    
    params, prestr = params_filter(params)
    
    params['sign'] = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
    params['sign_type'] = settings.ALIPAY_SIGN_TYPE
    
    return _GATEWAY + urlencode(params)


# 批量付款到支付宝账户有密接口
def batch_trans_notify (batch_no,batch_num,  detail_data):
    params = {}
    params['service']       = 'batch_trans_notify'

    # 获取配置文件
    params['partner']           = settings.ALIPAY_PARTNER
    params['_input_charset']    = settings.ALIPAY_INPUT_CHARSET

    params['notify_url']        = settings.ALIPAY_NOTIFY_URL_TEACHER_WITHDRAW

    params['account_name']     = settings.ACCOUNT_NAME.encode('utf-8')

    # detail_data 格式 流水号1^收款方账号1^收款账号姓名1^付款金额1^备注说明1

    detail_data_str = '%s^%s^%s^%s^%s' % (detail_data['fn'],
                                          detail_data['receiveraccount'],
                                          detail_data['name'].encode('utf-8'),
                                          detail_data['fee'],
                                          detail_data['desc'].encode('utf-8'),)
    params['detail_data']      = detail_data_str


    params['batch_no']         = batch_no
    params['batch_num']        = batch_num
    params['batch_fee']        = detail_data['fee']

    params['email']            = settings.ALIPAY_SELLER_EMAIL
    params['pay_date']         = datetime.date.today().strftime("%Y%m%d")

    params,prestr       = params_filter(params)
    params['sign']      = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
    params['sign_type'] = settings.ALIPAY_SIGN_TYPE

    return _GATEWAY + urlencode(params)


# 纯担保交易接口
def create_partner_trade_by_buyer (tn, subject, body, unitprice,quantity):
    params = {}
    # 基本参数
    params['service']       = 'create_partner_trade_by_buyer'
    params['partner']           = settings.ALIPAY_PARTNER
    params['_input_charset']    = settings.ALIPAY_INPUT_CHARSET
    params['notify_url']        = settings.ALIPAY_NOTIFY_URL_STUDENT_BUY_PRODUCT
    params['return_url']        = settings.ALIPAY_RETURN_URL_STUDENT_BUY_PRODUCT

    # 业务参数
    params['out_trade_no']  = tn        # 请与贵网站订单系统中的唯一订单号匹配
    params['subject']       = subject   # 订单名称，显示在支付宝收银台里的“商品名称”里，显示在支付宝的交易管理的“商品名称”的列表里。
    params['payment_type']  = '1'
    params['logistics_type'] = 'EXPRESS'   # 第一组物流类型
    params['logistics_fee'] = '0.00'
    params['logistics_payment'] = 'SELLER_PAY'
    params['price'] = unitprice             # 商品单位金额
    params['quantity'] = quantity              # 商品的数量
    params['seller_email']      = settings.ALIPAY_SELLER_EMAIL
    params['body']          = body      # 订单描述、订单详细、订单备注，显示在支付宝收银台里的“商品描述”里
    params['show_url'] = settings.ALIPAY_SHOW_URL
    
    params,prestr = params_filter(params)
    
    params['sign'] = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
    params['sign_type'] = settings.ALIPAY_SIGN_TYPE
    
    return _GATEWAY + urlencode(params)


# 确认发货接口
def send_goods_confirm_by_platform(tn):
    params = {}

    # 基本参数
    params['service']       = 'send_goods_confirm_by_platform'
    params['partner']           = settings.ALIPAY_PARTNER
    params['_input_charset']    = settings.ALIPAY_INPUT_CHARSET

    # 业务参数
    params['trade_no']  = tn
    params['logistics_name'] = '银河列车'   # 物流公司名称
    params['transport_type'] = 'POST'
    
    params,prestr = params_filter(params)
    
    params['sign'] = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
    params['sign_type'] = settings.ALIPAY_SIGN_TYPE
    
    return _GATEWAY + urlencode(params)


def notify_verify(post):
    # 初级验证--签名
    _,prestr = params_filter(post)
    if post.get('sign_type') == settings.ALIPAY_SIGN_TYPE:
        mysign = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
        if mysign != post.get('sign'):
            print('no sign')
            return False
    elif post.get('sign_type') == settings.ALIPAY_MOBILE_SIGN_TYPE:
        return mpay_rsa.verify(prestr,post.get('sign'))

    # 二级验证--查询支付宝服务器此条信息是否有效
    params = {}
    params['partner'] = settings.ALIPAY_PARTNER
    params['notify_id'] = post.get('notify_id')
    if settings.ALIPAY_TRANSPORT == 'https':
        params['service'] = 'notify_verify'
        gateway = 'https://mapi.alipay.com/gateway.do'
    else:
        gateway = 'http://notify.alipay.com/trade/notify_query.do'
    veryfy_result = urlopen(gateway, urlencode(params).encode('utf-8')).read().decode('utf-8')  # py3 str不支持
    if veryfy_result.lower().strip() == 'true':
        return True

    print('verify result no true!!')
    return False


def notify_verify_old(post):
    # 初级验证--签名
    _,prestr = params_filter(post)
    mysign = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
    if mysign != post.get('sign'):
        print('no sign')
        return False
    
    # 二级验证--查询支付宝服务器此条信息是否有效
    params = {}
    params['partner'] = settings.ALIPAY_PARTNER
    params['notify_id'] = post.get('notify_id')
    if settings.ALIPAY_TRANSPORT == 'https':
        params['service'] = 'notify_verify'
        gateway = 'https://mapi.alipay.com/gateway.do'
    else:
        gateway = 'http://notify.alipay.com/trade/notify_query.do'
    veryfy_result = urlopen(gateway, urlencode(params)).read()
    if veryfy_result.lower().strip() == 'true':
        return True

    print('verify result no true!!')
    return False


def gen_mobilepay_params(tn, subject, body, total_fee):
    """
    阿里移动支付获取参数接口， 这是老的支付流程https://opendocs.alipay.com/open/01lofl， 现在都升级为新版app支付：
    https://opendocs.alipay.com/open/204/105297
    :param tn:
    :param subject:
    :param body:
    :param total_fee:
    :return:
    """
    params = {'service': 'mobile.securitypay.pay', 'payment_type': '1', 'partner': settings.ALIPAY_PARTNER,
              'seller_id': settings.ALIPAY_SELLER_EMAIL, 'notify_url': settings.ALIPAY_MOBILE_NOTIFY,
              '_input_charset': settings.ALIPAY_INPUT_CHARSET, 'out_trade_no': tn, 'subject': subject, 'body': body,
              'total_fee': total_fee}

    params, prestr = params_filter(params)

    params['sign'] = mpay_rsa.sign(prestr)
    params['sign_type'] = settings.ALIPAY_MOBILE_SIGN_TYPE

    return urlencode(params)
